#include <stdlib.h>
#include <stdio.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
#include "LecteurCaractere.h"
#include "Valeur.h"
#include "LecteurSymbole.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudSeqInst::getValeur() {
    try {
	Valeur* valeur = new ValeurInt(0);
	for (unsigned int i = 0; i < tabInst.size(); i++)
	    valeur = tabInst[i]->getValeur(); // on evalue chaque instruction de la séquence
	return valeur; // par convention, resultat = valeur de la derniere instruction
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
    for (unsigned int i = 0; i < tabInst.size(); i++)
	tabInst[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable = variable;
    this->expression = expression;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudAffectation::getValeur() {
    try {
	Valeur* valeur = expression->getValeur(); // on évalue l'expression
	((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
	return valeur; // par convention, une affectation a pour valeur la valeur affectée
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Affectation" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
    expression->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire(Symbole operateur,
	Noeud* operandeGauche,
	Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudOperateurBinaire::getValeur() {
    Valeur* valeur = new ValeurInt(0);
    Valeur* og = operandeGauche->getValeur();
    Valeur* od = operandeDroit->getValeur();

    // lorsque l'on a affaire à deux entiers
    if (typeid (*od) == typeid (ValeurInt) && typeid (*og) == typeid (ValeurInt)) {
	if (this->operateur == "+") valeur = new ValeurInt((((ValeurInt*) og)->getValeur())+(((ValeurInt*) od)->getValeur()));
	else if (this->operateur == "-") valeur = new ValeurInt((((ValeurInt*) og)->getValeur())-(((ValeurInt*) od)->getValeur()));
	else if (this->operateur == "*") valeur = new ValeurInt((((ValeurInt*) og)->getValeur())*(((ValeurInt*) od)->getValeur()));
	else if (this->operateur == "/") {
	    try {
		if (((ValeurInt*) od)->getValeur() != 0)
		    valeur = new ValeurInt((((ValeurInt*) og)->getValeur()) / (((ValeurInt*) od)->getValeur()));
		else {
		    cout << "Erreur pendant l'interpretation : division par zero" << endl;
		}
	    } catch (int) {
		throw;
	    }
	} else if (this->operateur == "==") valeur = new ValeurInt((((ValeurInt*) og)->getValeur()) == (((ValeurInt*) od)->getValeur()));
	else if (this->operateur == "!=") valeur = new ValeurInt((((ValeurInt*) og)->getValeur()) != (((ValeurInt*) od)->getValeur()));
	else if (this->operateur == "<") valeur = new ValeurInt((((ValeurInt*) og)->getValeur())<(((ValeurInt*) od)->getValeur()));
	else if (this->operateur == "<=") valeur = new ValeurInt((((ValeurInt*) og)->getValeur()) <= (((ValeurInt*) od)->getValeur()));
	else if (this->operateur == ">") valeur = new ValeurInt((((ValeurInt*) og)->getValeur())>(((ValeurInt*) od)->getValeur()));
	else if (this->operateur == ">=") valeur = new ValeurInt((((ValeurInt*) og)->getValeur()) >= (((ValeurInt*) od)->getValeur()));
	else if (this->operateur == "ou") valeur = new ValeurInt((((ValeurInt*) og)->getValeur()) || (((ValeurInt*) od)->getValeur()));
	else if (this->operateur == "et") valeur = new ValeurInt((((ValeurInt*) og)->getValeur()) && (((ValeurInt*) od)->getValeur()));
    }
	// lorsque l'on a affaire à deux reels
    else if (typeid (*od) == typeid (ValeurFloat) && typeid (*og) == typeid (ValeurFloat)) {
	if (this->operateur == "+") valeur = new ValeurFloat((((ValeurFloat*) og)->getValeur())+(((ValeurFloat*) od)->getValeur()));
	else if (this->operateur == "-") valeur = new ValeurFloat((((ValeurFloat*) og)->getValeur())-(((ValeurFloat*) od)->getValeur()));
	else if (this->operateur == "*") valeur = new ValeurFloat((((ValeurFloat*) og)->getValeur())*(((ValeurFloat*) od)->getValeur()));
	else if (this->operateur == "/") {
	    try {
		if (((ValeurFloat*) od)->getValeur() != 0.0)
		    valeur = new ValeurFloat((((ValeurFloat*) og)->getValeur()) / (((ValeurFloat*) od)->getValeur()));
		else {
		    cout << "Erreur pendant l'interpretation : division par zero" << endl;
		}
	    } catch (int) {
		throw;
	    }
	} else if (this->operateur == "==") valeur = new ValeurInt((((ValeurFloat*) og)->getValeur()) == (((ValeurFloat*) od)->getValeur()));
	else if (this->operateur == "!=") valeur = new ValeurInt((((ValeurFloat*) og)->getValeur()) != (((ValeurFloat*) od)->getValeur()));
	else if (this->operateur == "<") valeur = new ValeurInt((((ValeurFloat*) og)->getValeur())<(((ValeurFloat*) od)->getValeur()));
	else if (this->operateur == "<=") valeur = new ValeurInt((((ValeurFloat*) og)->getValeur()) <= (((ValeurFloat*) od)->getValeur()));
	else if (this->operateur == ">") valeur = new ValeurInt((((ValeurFloat*) og)->getValeur())>(((ValeurFloat*) od)->getValeur()));
	else if (this->operateur == ">=") valeur = new ValeurInt((((ValeurFloat*) og)->getValeur()) >= (((ValeurFloat*) od)->getValeur()));
	else if (this->operateur == "ou") valeur = new ValeurInt((((ValeurFloat*) og)->getValeur()) || (((ValeurFloat*) od)->getValeur()));
	else if (this->operateur == "et") valeur = new ValeurInt((((ValeurFloat*) og)->getValeur()) && (((ValeurFloat*) od)->getValeur()));
    }
	// lorsque l'on a affaire à deux chaines
    else if (typeid (*od) == typeid (ValeurString) && typeid (*og) == typeid (ValeurString)) {
	if (this->operateur == "==") valeur = new ValeurInt((((ValeurString*) og)->getValeur()) == (((ValeurString*) od)->getValeur()));
	else if (this->operateur == "!=") valeur = new ValeurInt((((ValeurString*) og)->getValeur()) != (((ValeurString*) od)->getValeur()));
	else if (this->operateur == "+") valeur = new ValeurString( (((ValeurString*) og)->getValeur())+=(((ValeurString*) od)->getValeur()) );
	else if (this->operateur == ">") valeur = new ValeurInt((((ValeurString*) og)->getValeur().size())>(((ValeurString*) od)->getValeur().size()));
	else if (this->operateur == ">=") valeur = new ValeurInt((((ValeurString*) og)->getValeur().size()) >= (((ValeurString*) od)->getValeur().size()));
	else if (this->operateur == "<") valeur = new ValeurInt((((ValeurString*) og)->getValeur().size())<(((ValeurString*) od)->getValeur().size()));
	else if (this->operateur == ">=") valeur = new ValeurInt((((ValeurString*) og)->getValeur().size()) <= (((ValeurString*) od)->getValeur().size()));
    }
	// lorsque l'on a affaire à deux caractères
    else if (typeid (*od) == typeid (ValeurChar) && typeid (*og) == typeid (ValeurChar)) {
	if (this->operateur == "==") valeur = new ValeurInt((((ValeurChar*) og)->getValeur()) == (((ValeurChar*) od)->getValeur()));
	else if (this->operateur == "!=") valeur = new ValeurInt((((ValeurChar*) og)->getValeur()) != (((ValeurChar*) od)->getValeur()));
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurUnaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurUnaire::NoeudOperateurUnaire(Symbole operateur, Noeud* operande) {
    this->operateur = operateur;
    this->operande = operande;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudOperateurUnaire::getValeur() {
    try {
	Valeur* valeur = new ValeurInt(0);
	Valeur* o = new ValeurInt(((ValeurInt*) operande)->getValeur());
	if (this->operateur == "-") valeur = new ValeurInt(0 - ((ValeurInt*) o)->getValeur());
	else // this->operateur=="non"
	    if (((ValeurInt*) o)->getValeur() != 1)
	    valeur = new ValeurInt(0);
	else
	    valeur = new ValeurInt(1);
	return valeur;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurUnaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Unaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operande->afficher(indentation + 1); // on affiche le fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudIntSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi(Noeud* expbool, Noeud* seqInst, Noeud* siSuivant) {
    this->expBool = expbool;
    this->seqInst = seqInst;
    this->siSuivant = siSuivant;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstSi::getValeur() {
    try {
	Valeur* valeur = new ValeurInt(0);
	if (((ValeurInt*) expBool->getValeur())->getValeur() == 1) valeur = seqInst->getValeur();
	else if (siSuivant != NULL) siSuivant->getValeur();
	return valeur; // par convention, une affectation a pour valeur la valeur affectée
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstSi::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction si applique a : " << endl;
    expBool->afficher(indentation + 1); // on affiche fils gauche et fils droit
    seqInst->afficher(indentation + 1); // en augmentant l'indentation
    if (siSuivant != NULL) siSuivant->afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* affectation, Noeud* expbool, Noeud* affectationDroite, Noeud* seqInst) {
    this->affectation = affectation;
    this->expBool = expbool;
    this->affectationDroite = affectationDroite;
    this->seqInst = seqInst;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstPour::getValeur() {
    try {
	Valeur* valeur = new ValeurInt(0);
	for (affectation->getValeur(); ((ValeurInt*) expBool->getValeur())->getValeur(); affectationDroite->getValeur())
	    valeur = seqInst->getValeur();
	return valeur;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstPour::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Pour appliquee a : " << endl;
    affectation->afficher(indentation + 1);
    expBool->afficher(indentation + 1);
    affectationDroite->afficher(indentation + 1);
    seqInst->afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstTantQue
////////////////////////////////////////////////////////////////////////////////

NoeudInstTantQue::NoeudInstTantQue(Noeud* expbool, Noeud* seqinst) {
    this->expBool = expbool;
    this->seqInst = seqinst;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstTantQue::getValeur() {
    try {
	Valeur* valeur = new ValeurInt(0);
	while (((ValeurInt*) expBool->getValeur())->getValeur() == 1) {
	    valeur = seqInst->getValeur();
	}
	return valeur;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstTantQue::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction TantQue appliquee a : " << endl;
    expBool->afficher(indentation + 1);
    seqInst->afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter(Noeud* expbool, Noeud* seqinst) {
    this->expBool = expbool;
    this->seqInst = seqinst;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstRepeter::getValeur() {
    try {
	Valeur* valeur = new ValeurInt(0);
	do {
	    valeur = seqInst->getValeur();
	} while (((ValeurInt*) expBool->getValeur())->getValeur() == 1);
	return valeur;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstRepeter::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Repeter appliquee a : " << endl;
    expBool->afficher(indentation + 1);
    seqInst->afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////

NoeudInstSwitch::NoeudInstSwitch(Noeud* variable) :
vectConditions(), vectInsts() {
    this->variable = variable;
}

void NoeudInstSwitch::ajouteCase(Noeud* condition, Noeud* seqInst) {
    vectConditions.push_back(condition);
    vectInsts.push_back(seqInst);
}

Valeur* NoeudInstSwitch::getValeur() {
    try {
	vector<Noeud*>::iterator itC;
	int i = 0;
	Valeur* re = new ValeurInt(0);

	for (itC = vectConditions.begin(); itC != vectConditions.end(); itC++) {
	    if (((ValeurInt*) variable->getValeur())->getValeur() == ((ValeurInt*) (*itC)->getValeur())->getValeur()) {
		re = vectInsts[i]->getValeur();
		break;
	    }
	    i++;
	}
	return re;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstSwitch::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Switch appliquee a : " << endl;
    for (unsigned int i = 0; i < vectConditions.size(); i++) {
	vectConditions[i]->afficher(indentation + 1);
	vectInsts[i]->afficher(indentation + 1);
    }
}

////////////////////////////////////////////////////////////////////////////////
// NoeudEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire::NoeudInstEcrire(Noeud* phrase) {
    this->phrase = phrase;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstEcrire::getValeur() {
    try {
	Valeur* valeur = NULL;
	if (typeid (*phrase->getValeur()) == typeid (ValeurInt)) {
	    cout << ((ValeurInt*) phrase->getValeur())->getValeur();
	} else if (typeid (*phrase->getValeur()) == typeid (ValeurFloat)) {
	    cout << ((ValeurFloat*) phrase->getValeur())->getValeur();
	} else if (typeid (*phrase->getValeur()) == typeid (ValeurString)) {
	    cout << ((ValeurString*) phrase->getValeur())->getValeur();
	} else if (typeid (*phrase->getValeur()) == typeid (ValeurChar)) {
	    cout << ((ValeurChar*) phrase->getValeur())->getValeur();
	}
	return valeur; // par convention, une affectation a pour valeur la valeur affectée
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstEcrire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Ecrire appliquee a : " << endl;
    phrase->afficher(indentation + 1); // on affiche l'expression a ecrire en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudLire
////////////////////////////////////////////////////////////////////////////////

NoeudInstLire::NoeudInstLire(Noeud* variable) {
    this->variable = variable;
}

////////////////////////////////////////////////////////////////////////////////

Valeur* NoeudInstLire::getValeur() {
    Valeur* valeur = new ValeurInt(0);
    string s;
        variable->afficher();
        cout << "Valeur ?" << endl;
        cin >> s;
    if (isdigit(s[0]) && Symbole::isFloat(s)) {
	float val = atof(s.c_str());
	valeur = new ValeurFloat(val);
    } else if (isdigit(s[0])) {
        int val = atoi(s.c_str());
	valeur = new ValeurInt(val);
    } else if (Symbole::isChar(s)) {
	char val = s[1];
	valeur = new ValeurChar(val);
    } else {
	valeur = new ValeurString("\""+s+"\"");
    }
    
    try {
	((SymboleValue*) variable)->setValeur(valeur);
	return valeur;
    } catch (char const*) {
	throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstLire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Lire appliquee a : " << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
}


